#pragma once 
#include"sock.hpp"
#include<sys/select.h>

namespace ns_select
{
    using namespace ns_sock;
    const int default_port=8081;
#define NUM (sizeof(fd_set) * 8)

    class Select
    {
    private:
        uint16_t _port;
        int _listen_sock;
        int fd_array[NUM];//保存需要关注的文件的对应文件描述符
    public:
        Select(uint16_t port=default_port):_port(port),_listen_sock(-1){
            for(int i=0;i<NUM;i++) 
                fd_array[i]=-1;//初始化文件描述符数组
        }
        ~Select(){
            if(_listen_sock>0) close(_listen_sock);
        }
        void InitSelectServer()
        {
            _listen_sock=Sock::Socket();
            Sock::Bind(_listen_sock,_port);
            Sock::Listen(_listen_sock);
            //将监听套接字保存
            fd_array[0]=_listen_sock;
        }
        void PrintFd()
        {   
            std::cout<<"fd_array:";
            for(int i=0;i<NUM;i++)
            {
                if(fd_array[i]!=-1)
                    std::cout<<fd_array[i]<<" ";
            }
            std::cout<<std::endl;;
        }
        void HandlerEvent(const fd_set& rfds)
        {
            for(int i=0;i<NUM;i++)
            {
                if(fd_array[i]==-1) continue;
                if(FD_ISSET(fd_array[i],&rfds))
                {
                    if(fd_array[i]==_listen_sock){//监听套接字的连接到来
                        struct sockaddr_in peer;
                        socklen_t len=sizeof(peer);
                        bzero(&peer,len);
                        int sock=accept(_listen_sock,(struct sockaddr*)&peer,&len);
                        if(sock>0){
                            //添加文件
                            int j=0;
                            for(;j<NUM;j++)
                            {
                                if(fd_array[j]>0) continue;
                                if(fd_array[j]==-1){
                                    fd_array[j]=sock;
                                    break;
                                }
                            }
                            if(j==NUM){
                                std::cerr<<"fd_array 已满"<<std::endl;
                                close(sock);//关闭文件
                            }
                            else{
                                std::cout<<"添加新连接:"<<sock<<std::endl;
                                PrintFd();
                            }
                        }else{
                            std::cerr<<"accept err"<<std::endl;
                            //异常处理...
                        }
                    }
                    else{//数据到来-进行数据读取
                        char buffer[1024]={0};
                        ssize_t s=recv(fd_array[i],buffer,sizeof(buffer),0);
                        if(s>0){
                            buffer[s-1]=0;
                            std::cout<<"buffer:"<<buffer<<std::endl;
                        }else if(s==0){//对端连接关闭
                            std::cerr<<"peer sock:"<<fd_array[i]<<" close..."<<std::endl;
                            close(fd_array[i]);//关闭连接
                            fd_array[i]=-1;//去除保存的数据
                            PrintFd();
                        }else{
                            std::cerr<<"recv err..."<<std::endl;
                        }
                    }
                }
            }
        }
        void Loop()
        {
            fd_set rfds;//读事件位图
            while(true){
                //初始化读事件位图
                int maxfd=-1;
                for(int i=0;i<NUM;i++) 
                {
                    if(fd_array[i]==-1) continue;
                    FD_SET(fd_array[i],&rfds);
                    if(maxfd<fd_array[i]) 
                        maxfd=fd_array[i];
                }
                //多路转接
                int n=select(maxfd+1,&rfds,nullptr,nullptr,nullptr);//采用阻塞式关注文件的读事件，有读事件就绪返回
                if (n>0){//有事件就绪
                    HandlerEvent(rfds);
                }
                else if(n==0)
                {
                    std::cout<<"timeout..."<<std::endl;
                }
                else
                {
                    std::cerr<<"select err"<<errno<<std::endl;
                }             
            }
        }
    } ;
}